<template>
  <div class="cache-demo">
    <el-scrollbar>
      <div class="img">
        <img src="/api/public/source/dark.png" alt="dark" />
      </div>
      <br />
      <hr />
      <br />
      <ul>
        <li>
          <p><strong>在输入url的时候，会进行本地历史记录和标签页的查询，提供模糊查询效果</strong></p>
        </li>
        <li>
          <p>
            <strong>浏览器查找当前URL是否存在缓存，并比较缓存是否过期</strong><br />
            HTTP缓存有多种规则，根据是否需要向服务器重新发起请求来分类，将其分为强制缓存，对比缓存。<br />
            强制缓存：判断HTTP首部字段：Expires 和 cache-control。<br />
            Expires是一个绝对时间，即服务器时间。浏览器检查当前时间，如果还没到失效时间就直接使用缓存文件。但是该方法存在一个问题：服务器时间与客户端时间可能不一致<br />
            cache-control中的max-age保存了一个相对时间。例如Cache-Control: max-age = 484200，表示浏览器收到文件后，缓存在484200s内有效。
            如果同时存在cache-control和Expires，浏览器总是优先使用cache-control。<br />
            对比缓存：通过HTTP的 last-modified（最新-改进），Etag字段进行判断。<br />
            last-modified
            表示请求的URL（资源）最后一次更新的时间。下一次浏览器请求资源时就发送if-modified-since字段。服务器用本地Last-modified时间与if-modified-since时间比较，如果不一致则认为缓存已过期并返回新资源给浏览器；如果时间一致则发送304状态码，让浏览器继续使用缓存。<br />
            Etag：资源的实体标识（哈希字符串），当资源内容更新时，Etag会改变。服务器会判断Etag是否发生变化，如果变化则返回新资源，否则返回304。
          </p>
        </li>
        <li>
          <p>
            <strong>DNS解析URL对应的IP</strong><br />
            首先查询本地host文件是否有对应地址的一个映射关系，如果没有找到，会查找本地DNS解析器缓存，如果还是没有找到则会查找本地DNS服务器，最后迭代查询，按根域服务器库（.com,.cn,.vip,.top...）-&gt;顶级域(com)-&gt;-&gt;第二层域子域（baidu.com）,最后根据baidu.com的域名找到相应的IP，返回给浏览器。
          </p>
        </li>
        <li>
          <p>
            <strong>根据IP建立TCP连接（三次握手）</strong><br />
            浏览器用这个IP地址与服务器建立TCP连接，如果用的是HTTPS，还有完成TLS / SSL的握手。<br />
            第一次握手： 建立连接时，客户端发送syn包（syn=j）到服务器，并进入等待服务器确认的状态；<br />
            第二次握手： 服务器收到syn包，必须确认客户端的syn（ack=j+1），同时自己根据syn生成一个ACK包，此时服务器进入等待状态；<br />
            第三次握手： 客户端收到服务器的ACK包，向服务器发送确认，此包发送完毕，客户端和服务器进入ESTABLISHED（TCP连接成功）状态，完成三次握手。
          </p>
        </li>
        <li>
          <p>
            <strong>发送HTTP请求</strong><br />
            在建立好连接以后呢，构造HTTP请求，在构造请求的过程中，要填充少量至HTTP头部
          </p>
        </li>
        <li>
          <p>
            <strong>服务器解析请求返回结果</strong><br />
            然后通过这个TCP连接发起HTTP请求，当服务器收到这个HTTP请求以后，返回给浏览器以HTTP页面作为包体的HTTP响应。
          </p>
        </li>
        <li>
          <p>
            <strong>浏览器解析结果并渲染页面</strong><br />
            根据html代码生成DOM树
            ，在解析到外部的css和js文件时，向服务器发起请求下载资源，如果是CSS代码会在下载的同时进行解析，如果是JS代码会阻塞页面加载，根据CSS代码生成OM树，然后生成layout树（重排），生成painting树（重绘），然后生成渲染树。
          </p>
        </li>
        <li>
          <p>
            <strong>关闭TCP连接（四次挥手）</strong><br />
            第一次握手是浏览器发完数据，然后发送FIN请求断开连接。<br />
            第二次握手是服务器向客户端发送ACK，表示同意。<br />
            第三次握手是服务器可能还有数据向浏览器发送，所以向浏览器发送ACK同时也发送FIN请求，是第三次握手。<br />
            第四次握手是浏览器接受返回的ACK，表示数据传输完成。
          </p>
        </li>
      </ul>
      <h3>强制缓存 与 协商缓存</h3>

      <br />
      <p><code>浏览器缓存(http 缓存)</code> 是指浏览器在本地磁盘对用户最近请求过的文档进行存储&#xff0c;当访问者再次访问同一页面时&#xff0c;浏览器就可以直接从本地磁盘加载文档。</p>
      <h3>优点</h3>
      <ol>
        <li><p>减少了冗余的数据传输&#xff0c;节省带宽&#xff0c;减少服务器压力</p></li>
        <li><p>加快了客户端加载速度&#xff0c;提升用户体验。</p></li>
      </ol>
      <h3>强缓存</h3>
      <p>强缓存不会向服务器发送请求&#xff0c;而是直接从缓存中读取资源&#xff0c;强缓存可以通过设置两种 HTTP Header 实现&#xff1a;Expires 和 Cache-Control&#xff0c;这两个头部分别是HTTP1.0和HTTP1.1的实现。</p>
      <p>Expires</p>
      <p>Expires是HTTP1.0提出的一个表示资源过期时间的header&#xff0c;它描述的是一个绝对时间&#xff0c;由服务器返回。Expires 受限于本地时间&#xff0c;如果修改了本地时间&#xff0c;就会造成缓存失效。</p>
      <p>Cache-Control</p>
      <p>如何查看 是 http1.0 还是 1.1 呢 打开浏览器控制台， network 中 Response Headers 点击 View parsed 一般会有</p>
      <p>Cache-Control 出现于 HTTP/1.1&#xff0c;常见字段是max-age&#xff0c;单位是秒&#xff0c;很多web服务器都有默认配置&#xff0c;优先级高于Expires&#xff0c;表示的是相对时间。</p>
      <p>Response Headers 中的返回的时间 Date: 是GMT, 这个是 格林威治什么时间 就是每个时区都不一样的 北京时区 是东八区 需要 标准的GMT(UTC) 加上 8 小时</p>
      <p>
        例如Cache-Control:max-age&#61;3600 代表资源的有效期是 3600 秒。取的是响应头中的 Date&#xff0c;请求发送的时间&#xff0c;表示当前资源在 Date ~ Date &#43;3600s 这段时间里都是有效的。Cache-Control
        还拥有多个值&#xff1a;
      </p>
      <ul>
        <li><p>no-cache 不直接使用缓存&#xff0c;也就是跳过强缓存。</p></li>
        <li><p>no-store 禁止浏览器缓存数据&#xff0c;每次请求资源都会向服务器要完整的资源。</p></li>
        <li><p>public 可以被所有用户缓存&#xff0c;包括终端用户和 CDN 等中间件代理服务器。</p></li>
        <li><p>private 只允许终端用户的浏览器缓存&#xff0c;不允许其他中间代理服务器缓存。</p></li>
        <li>
          <p>
            must-revalidate 用来表示在一个缓存过期之后，不能直接使用这个过期的缓存，必须校验之后才能使用。 must-revalidate生效的场景还有一个大前提，那就是 HTTP
            规范是允许客户端在某些特殊情况下直接使用过期缓存的，比如校验请求发送失败的时候，还比如有配置一些特殊指令（stale-while-revalidate、stale-if-error等）的时候，原文是这样的： A cache MUST NOT
            send stale responses unless it is disconnected (i.e., it cannot contact the origin server or otherwise find a forward path) or doing so is explicitly allowed
            而must-revalidate的作用就是让那个“unless”失效 ，带有 must-revalidate 的缓存，在任何情况下，都必须成功 revalidate 后才能使用，没有例外。
          </p>
        </li>
      </ul>
      <p><code>要注意的就是no-cache和no-store的区别&#xff0c;no-cache是跳过强缓存&#xff0c;还是会走协商缓存的步骤&#xff0c;而no-store是真正的完全不走缓存&#xff0c;所有资源都不会缓存在本地</code></p>
      <h3>协商缓存</h3>
      <p>
        当浏览器对某个资源的请求没有命中强缓存&#xff0c;就会发一个请求到服务器&#xff0c;验证协商缓存是否命中&#xff0c;如果协商缓存命中&#xff0c;请求响应返回的http状态为304并且会显示一个Not Modified的字符串。
      </p>
      <p>协商缓存用的是【Last-Modified&#xff0c;If-Modified-Since】和【ETag、If-None-Match】这两对Header来管理的。</p>
      <p><code>注意&#xff01;&#xff01;协商缓存需要配合强缓存使用&#xff0c;使用协商缓存需要先设置Cache-Control&#xff1a;no-cache或者pragma&#xff1a;no-cache来告诉浏览器不走强缓存</code></p>
      <p>Last-Modified、If-Modified-Since</p>
      <p>这两个Header是HTTP1.0版本提出来的&#xff0c;两个字段配合使用。</p>
      <p>
        Last-Modified
        表示本地文件最后修改日期&#xff0c;浏览器会在请求头带上If-Modified-Since&#xff08;上次返回的Last-Modified的值&#xff09;&#xff0c;服务器会将这个值与资源修改的时间匹配&#xff0c;如果时间不一致&#xff0c;服务器会返回新的资源&#xff0c;并且将
        Last-Modified 值更新&#xff0c;作为响应头返回给浏览器。如果时间一致&#xff0c;表示资源没有更新&#xff0c;服务器返回 304 状态码&#xff0c;浏览器拿到响应状态码后从本地缓存中读取资源。
      </p>
      <p>但Last-Modified有几个问题。</p>
      <ul>
        <li><p>文件虽然被修改了&#xff0c;但最终的内容没有变化&#xff0c;这样文件修改时间还是会被更新</p></li>
        <li><p>有的文件修改频率在秒以内&#xff0c;这时候以秒粒度来记录就不够了</p></li>
        <li><p>有的服务器无法精确获取文件的最后修改时间。</p></li>
      </ul>
      <p>所以出现了ETAG。</p>
      <p>ETag、If-None-Match</p>
      <p>
        在HTTP1.1版本中&#xff0c;服务器通过 Etag 来设置响应头缓存标识。Etag 的值由服务端生成。在第一次请求时&#xff0c;服务器会将资源和 Etag
        一并返回给浏览器&#xff0c;浏览器将两者缓存到本地缓存数据库。在第二次请求时&#xff0c;浏览器会将 Etag 信息放到 If-None-Match
        请求头去访问服务器&#xff0c;服务器收到请求后&#xff0c;会将服务器中的文件标识与浏览器发来的标识进行对比&#xff0c;如果不相同&#xff0c;服务器返回更新的资源和新的 Etag &#xff0c;如果相同&#xff0c;服务器返回
        304 状态码&#xff0c;浏览器读取缓存。
      </p>
      <p><img alt="" src="https://img-blog.csdnimg.cn/img_convert/4b587a6ab4e0bbbe07c465c0fcbb9668.png" /></p>
      <h3>流程总结</h3>
      <p><img alt="" src="https://img-blog.csdnimg.cn/img_convert/c1aa65ee1b850e20fb870352db3c3a24.png" /></p>
      <p>总结这几个字段&#xff1a;</p>
      <ul>
        <li><p>Cache-Control —— 请求服务器之前</p></li>
        <li><p>Expires —— 请求服务器之前</p></li>
        <li><p>If-None-Match (Etag) —— 请求服务器</p></li>
        <li><p>If-Modified-Since (Last-Modified) —— 请求服务器</p></li>
      </ul>
      <h2>总结</h2>
      <p>通常web服务器都有默认的缓存配置&#xff0c;具体的实现可能也不大相同&#xff0c;像nginx、tomcat、express等web服务器都有相应的源码&#xff0c;</p>
      <p>
        合理的使用强缓存和协商缓存具体需要看项目的使用场景和需求。像目前常见的单页面应用&#xff0c;因为通常打包都是新生成html与相应的静态资源依赖&#xff0c;所以可以对html文件配置协商缓存&#xff0c;而打包生成的依赖&#xff0c;例如js、css这些文件可以使用强缓存。或者只对第三方库使用强缓存&#xff0c;因为第三方库通常版本更新较慢&#xff0c;可以锁定版本。
      </p>
    </el-scrollbar>
  </div>
</template>

<script>
export default {
  // 组件名称
  name: 'cacheDemo',
  // 局部注册的组件
  components: {},
  // 组件参数 接收来自父组件的数据
  props: {},
  // 组件状态值
  data() {
    return {}
  },
  // 计算属性
  computed: {},
  // 侦听器
  watch: {},
  // 生命周期钩子   注：没用到的钩子请自行删除
  /**
   * 组件实例创建完成，属性已绑定，但DOM还未生成，$ el属性还不存在
   */
  created() {},
  /**
   * el 被新创建的 vm.el 替换，并挂载到实例上去之后调用该钩子。
   * 如果 root 实例挂载了一个文档内元素，当 mounted 被调用时 vm.el 也在文档内。
   */
  mounted() {},
  // 组件方法
  methods: {}
}
</script>

<style scoped lang="less">
.cache-demo {
  box-sizing: border-box;
  width: 100%;
  overflow: none;
  /* position: absolute;
  .el-scrollbar {
    height: 100%;
  } */
  background-color: #ced6e0;
  .img {
    width: 700px;
    height: 400px;
    img {
      width: 100%;
      height: 100%;
    }
  }
  p,
  li {
    padding-bottom: 5px;
  }
}
</style>
